/*
 * File:    Statistics.java
 * Created: 03-Oct-2006
 * Version: $Id$
 *
 * COPYRIGHT (C) 2006, Bitgate Software, LLC.  All Rights Reserved.
 *
 * software@bitgatesoftware.com
 */

package com.bitgate.util.statistics;

import static com.bitgate.util.debug.Debug.*;

import java.util.ArrayList;

import com.bitgate.util.config.Config;
import com.bitgate.util.scheduler.Scheduler;
import com.bitgate.util.scheduler.SchedulerInterface;
import com.bitgate.util.scheduler.SchedulerTimeContext;

/**
 * This is the main statistics server that runs in tandem with the system itself.  This code is automatically started when
 * anything touches the statistics interface, the statistics server starts.  It uses a main function to register objects
 * for statistical output, which is &quot;register&quot;.  Statistics are globally written (at least right now) during a
 * snapshot period in time, which pings all of the registered statistic objects, and writes their combined stats to 
 * either the console or a single file.
 *
 * @author Kenji Hollis &lt;kenji@bitgatesoftware.com&gt;
 * @version $Id$
 */
public class Statistics implements SchedulerInterface
{
    private static final Statistics _default = new Statistics();
    private final ArrayList<StatisticsInterface> registeredObjects;
    private String outputMethod;

    /**
     * Constructor - starts the Statistics server, and registers the config files information for setup.
     */
    private Statistics()
    {
		this.registeredObjects = new ArrayList<StatisticsInterface>();
		
		if (isDebugEnabled()) {
			debug("Statistics server instantiated.");
		}
	
		String statisticsConfig = System.getProperty("statistics.config");
	
		if (statisticsConfig == null) {
		    statisticsConfig = "conf/defaults/statistics.xml";
		}
	
		Config.getDefault().register(statisticsConfig, "statistics");
	
		String secondStats = Config.getDefault().parseConfig("statistics", "/statistics/object[@type='tuning']/property" +
		    "[@name='statistics.interval-sec']/@value");
		String minuteStats = Config.getDefault().parseConfig("statistics", "/statistics/object[@type='tuning']/property" +
		    "[@name='statistics.interval-min']/@value");
		String hourStats = Config.getDefault().parseConfig("statistics", "/statistics/object[@type='tuning']/property" +
		    "[@name='statistics.interval-hour']/@value");
		String dayStats = Config.getDefault().parseConfig("statistics", "/statistics/object[@type='tuning']/property" +
		    "[@name='statistics.interval-day']/@value");
		String weekStats = Config.getDefault().parseConfig("statistics", "/statistics/object[@type='tuning']/property" +
		    "[@name='statistics.interval-week']/@value");
		String monthStats = Config.getDefault().parseConfig("statistics", "/statistics/object[@type='tuning']/property" +
		    "[@name='statistics.interval-month']/@value");
	
		Scheduler.getDefault().addTask(this, new SchedulerTimeContext(secondStats, minuteStats, hourStats, dayStats, weekStats, 
		    monthStats));
	
		this.outputMethod = Config.getDefault().parseConfig("statistics", "/statistics/object[@type='output']/property" +
		    "[@name='statistics.method']/@value");
		
		if (this.outputMethod == null || "".equals(this.outputMethod)) {
		    this.outputMethod = "console";
		    warning("No output method specified in statistics.xml for output; defaulting to 'console'");
		}
    }

    /**
     * Returns the default instance of the <code>Statistics</code> object.
     *
     * @return <code>static Statistics</code> object.
     */
    public static Statistics getDefault()
    {
    	return _default;
    }

    /**
     * Registers the class to the system so that statistics can be output when called.  Since these objects are added in an
     * <code>ArrayList</code> object, they are called and performed one-at-a-time.
     *
     * @param si The <code>StatisticsInterface</code> object to add.
     */
    public void register(StatisticsInterface si)
    {
    	this.registeredObjects.add(si);
    	
    	if (isDebugEnabled()) {
    		debug("Registered statistics object for object '" + si + "'");
    	}
    }

    /**
     * Triggers the scheduled function from the scheduler interface.
     */
    public void trigger()
    {
		ArrayList<String> statisticsOutput = new ArrayList<String>();
		int added = 0, skipped = 0;
	
		for(StatisticsInterface si : this.registeredObjects) {
		    ArrayList<String> stats = si.getStatistics();
	
		    if (stats != null) {
				statisticsOutput.addAll(stats);
				added++;
		    } else {
		    	skipped++;
		    }
		}
	
		if ("console".equalsIgnoreCase(this.outputMethod) && added > 0) {
		    for(String str : statisticsOutput) {
		    	if (isDebugEnabled()) {
		    		debug("Statistics: " + str);
		    	}
		    }
		}
	
		if (added > 0) {
			if (isDebugEnabled()) {
				debug("Benchmark: Added '" + added + "' Skipped '" + skipped + "'");
			}
		}
    }

    /**
     * Returns &quot;Statistics&quot; as the task name.
     *
     * @return <code>String</code> containing the current task name.
     */
    public String getTaskName()
    {
    	return "Statistics";
    }
}
